#include "stdafx.h"

#pragma managed
#include "ClassType.h"
#include "..\Util.h"
using namespace System::Text;
using namespace System::Diagnostics;

namespace ManagedAPI
{
	// static method
	ClassType^ ClassType::Create(const UdbEntity& entity)
	{
		Debug::Assert(entity != nullptr);
		if (entity == nullptr)
		{
			return nullptr;
		}

		char* keystr = udbEntityNameUnique(entity);
		String^ key = gcnew String(keystr);

		ClassType^ result = nullptr;
		if (mFileList->ContainsKey(key))
		{
			result = mFileList[key];
			Debug::Assert(result != nullptr);
		}
		else
		{
			result = gcnew ClassType(entity);
			mFileList->Add(key, result);
		}
		return result;
	}


	void ClassType::Clear()
	{
		mFileList->Clear();
	}

	ClassType::ClassType(const UdbEntity& entity)
		: Entity(entity)
	{
		
	}
	String^ ClassType::ToString()
	{
		StringBuilder^ sb = gcnew StringBuilder();

		sb->Append("class ");
		sb->Append( this->NameLong );
			
		return sb->ToString();
	}

	//bool ClassType::IsDefinedIn(String^ kind_string)
	//{
	//	Reference^ definedInRef = this->DefineInReference;
	//	Debug::Assert( definedInRef != nullptr );
	//	if (nullptr == definedInRef)
	//		return false;

	//	Entity^ definedInRefEnt = definedInRef->ThisEntity;
	//	Debug::Assert( definedInRefEnt != nullptr );
	//	String^ kindName = definedInRefEnt->KindName;
	//	bool result = false;
	//	if ( kindName->IndexOf(kind_string, StringComparison::OrdinalIgnoreCase) != -1)
	//	{
	//		result = true;
	//	}
	//	return result;
	//}
	bool ClassType::IsDefinedInsideClass::get()
	{
		String^ kind = this->KindName;
		bool result = false;
		if (kind == nullptr)
			return result;
		
		// Maybe this heuristic doesn't work, but we will see.
		if (kind->Contains("private") || kind->Contains("Private") || 
			kind->Contains("protected") || kind->Contains("Protected") ||
			kind->Contains("public") || kind->Contains("Public"))
		{
			result = true;
		}
		return result;
	}
	//bool ClassType::IsDefinedInsideNamespace::get()
	//{
	//	return IsDefinedIn("Namespace");
	//}	

	template<typename T> array<T^>^ ClassType::GetTypes(String^ refkinds, String^ entkinds)
	{
		UdbEntity thisEnt  = GetUdbEntity(); // Get a non-cached UdbEntity
		char* tempRefKinds = Util::StringToChar(refkinds);
		char* tempEntKinds = Util::StringToChar(entkinds);
		int unique         = 1;
		UdbReference* refs = nullptr;
		int numRefs = udbEntityRefs(thisEnt, tempRefKinds, tempEntKinds, unique, &refs);
		// free the native strings
		Util::FreeNativeString(tempRefKinds);
		tempRefKinds = nullptr;
		Util::FreeNativeString(tempEntKinds);
		tempEntKinds = nullptr;

		List<T^>^ result = gcnew List<T^>();
		if ( (numRefs > 0) && (refs != nullptr) )
		{
			for (int i = 0; i < numRefs; i++)
			{
				UdbReference ref = refs[i];
				Debug::Assert(ref != nullptr);
				UdbEntity entity = udbReferenceEntity(ref);
				Debug::Assert(entity != nullptr);
				T^ ent = T::Create(entity);
				Debug::Assert(ent != nullptr, "Unable to create Type");
				result->Add(ent);
			}
		}
		udbListReferenceFree(refs);
		refs = nullptr;

		return result->ToArray();
	}
	array<MethodType^>^ ClassType::GetMethods()
	{
		if (mMethodTypes == nullptr)
		{
			mMethodTypes = GetTypes<MethodType>("declare, define","Member Function" );
		}
		return mMethodTypes;
	}

	array<Entity^>^ ClassType::GetFields()
	{
		if (mFields == nullptr)
		{
			array<Reference^>^ refs = Refs("define, declare", "Member Object ~UnResolved ~UnKnown, Member Object Static" );
			mFields = Util::ReferenceToEntityArray(refs);
		}
		return mFields;
	}

	array<EnumType^>^ ClassType::GetEnums()
	{
		if (mEnums == nullptr)
		{
			mEnums = GetTypes<EnumType>("define", "Member Enum");
		}
		return mEnums;
	}
	array<Entity^>^ ClassType::GetUnions()
	{
		if (mUnions == nullptr)
		{
			array<Reference^>^ refs = Refs("define", "Member Union");
			mUnions = Util::ReferenceToEntityArray(refs);
		}
		return mUnions;
	}
	array<ClassType^>^ ClassType::GetBaseClasses()
	{
		if (mBaseClasses == nullptr)
		{
			mBaseClasses = GetTypes<ClassType>("Base", "Class");
		}
		return mBaseClasses;
	}
	array<ClassType^>^ ClassType::GetAllBaseClasses()
	{
		if (mAllBaseClasses == nullptr)
		{
			List<ClassType^>^ parentClassList = gcnew List<ClassType^>();
			RecurseOperation(this, parentClassList, true);
			mAllBaseClasses = parentClassList->ToArray();
		}
		return mAllBaseClasses;
	}

	array<ClassType^>^ ClassType::GetAllDerivedClasses()
	{
		if (mAllDerivedClasses == nullptr)
		{
			List<ClassType^>^ deriveClasses = gcnew List<ClassType^>();
			RecurseOperation(this, deriveClasses, false);
			mAllDerivedClasses = deriveClasses->ToArray();
		}
		return mAllDerivedClasses;
	}
	array<ClassType^>^ ClassType::GetDerivedClasses()
	{
		if (mDerivedClasses == nullptr)
		{
			mDerivedClasses = GetTypes<ClassType>("Derive", "Class");
		}
		return mDerivedClasses;
	}

	void ClassType::RecurseOperation(ClassType^ pClassEnt, List<ClassType^>^ recursiveCollection, bool pBaseRecurse)
	{
		array<ClassType^>^ classes = nullptr;
		if (pBaseRecurse)
			classes = pClassEnt->GetBaseClasses();
		else
			classes = pClassEnt->GetDerivedClasses();

		Debug::Assert(classes != nullptr);

		if ((classes != nullptr) && (classes->Length > 0))
		{
			for each (ClassType^ class_ent in classes)
			{
				// Check if it's already there, because we don't want to add a class twice
				if (false == recursiveCollection->Contains(class_ent))
				{
					recursiveCollection->Add(class_ent);
				}

				// Recursive call
				RecurseOperation(class_ent, recursiveCollection, pBaseRecurse);
			}
		}
	}
}
